/**
 * HLS 下载器核心功能
 * 支持 M3U8 播放列表解析和分片下载
 */
class HLSDownloader {
  constructor() {
    this.downloadQueue = new Map();
    this.activeDownloads = new Map();
    this.maxConcurrentDownloads = 3;
    this.retryAttempts = 3;
    this.chunkSize = 1024 * 1024; // 1MB chunks
    // AES-128 key cache for encrypted HLS
    this.keyCache = new Map();
    this.sequenceStart = 0;
  }

  /**
     * 开始下载 HLS 流
     * @param {Object} streamData - 流数据
     * @param {Function} progressCallback - 进度回调
     * @param {Function} completeCallback - 完成回调
     * @param {Function} errorCallback - 错误回调
     */
  async startDownload(streamData, progressCallback, completeCallback, errorCallback) {
    const downloadId = this.generateDownloadId();

    try {
      const downloadTask = {
        id: downloadId,
        streamData,
        progressCallback,
        completeCallback,
        errorCallback,
        status: 'initializing',
        progress: 0,
        totalSize: 0,
        downloadedSize: 0,
        segments: [],
        downloadedSegments: 0,
        startTime: Date.now()
      };

      this.activeDownloads.set(downloadId, downloadTask);

      // 解析 M3U8 播放列表
      const playlist = await this.parseM3U8(streamData.url, streamData.headers);
      downloadTask.segments = playlist.segments;
      downloadTask.totalSize = playlist.totalSize;
      downloadTask.sequenceStart = playlist.sequenceStart;
      downloadTask.baseUrl = playlist.baseUrl;
      downloadTask.status = 'downloading';

      // 开始下载分片
      await this.downloadSegments(downloadTask);

    } catch (error) {
      console.error('HLS download failed:', error);
      if (errorCallback) {
        errorCallback(error);
      }
      this.activeDownloads.delete(downloadId);
    }

    return downloadId;
  }

  /**
     * 解析 M3U8 播放列表
     * @param {string} url - M3U8 URL
     * @param {Object} headers - 请求头
     */
  async parseM3U8(url, headers = {}) {
    const response = await fetch(url, { headers });
    if (!response.ok) throw new Error(`Failed to fetch playlist: ${response.status}`);
    const content = await response.text();
    const lines = content.split('\n').map(l => l.trim()).filter(l => l.length > 0);

    const segments = [];
    let totalDuration = 0;
    const lastSlash = url.lastIndexOf('/');
    const baseUrl = lastSlash >= 0 ? url.slice(0, lastSlash + 1) : url;
    let mediaSequence = 0; // #EXT-X-MEDIA-SEQUENCE
    let currentKey = null; // { method, uri, ivHex }

    for (let i = 0; i < lines.length; i++) {
      const line = lines[i];
      if (line.startsWith('#EXT-X-MEDIA-SEQUENCE')) {
        const parts = line.split(':');
        if (parts[1]) mediaSequence = parseInt(parts[1].trim(), 10) || 0;
        continue;
      }
      if (line.startsWith('#EXT-X-KEY')) {
        const attrs = this.parseAttributeList(line);
        const method = (attrs.METHOD || attrs.method || '').replace(/"/g, '');
        if (method === 'AES-128') {
          const rawUri = (attrs.URI || attrs.uri || '').replace(/"/g, '');
          const ivRaw = (attrs.IV || attrs.iv || '').replace(/^0x/i, '');
          currentKey = {
            method: 'AES-128',
            uri: this.resolveUrl(rawUri, baseUrl),
            ivHex: ivRaw || null
          };
        } else {
          currentKey = null;
        }
        continue;
      }

      if (line.startsWith('#EXTINF:')) {
        const duration = parseFloat(line.split(':')[1].split(',')[0]);
        const nextLine = lines[i + 1];
        if (!nextLine || nextLine.startsWith('#')) continue;
        const segmentUrl = nextLine;
        const fullUrl = /^https?:\/\//i.test(segmentUrl) ? segmentUrl : (baseUrl + segmentUrl);
        const index = segments.length;
        const segment = { url: fullUrl, duration, index };
        if (currentKey) {
          segment.encryption = {
            ...currentKey,
            sequenceNumber: mediaSequence + index
          };
        }
        segments.push(segment);
        totalDuration += isNaN(duration) ? 0 : duration;
        i++; // skip URL line
      }
    }

    // Estimate total size if Content-Length not available; coarse 1MB/segment
    const totalSize = segments.length * 1024 * 1024;

    return { segments, totalDuration, totalSize, baseUrl, sequenceStart: mediaSequence };
  }

  /**
     * 下载所有分片
     * @param {Object} downloadTask - 下载任务
     */
  async downloadSegments(downloadTask) {
    const { segments, streamData } = downloadTask;
    const downloadedChunks = [];

    // 并发下载分片
    const downloadPromises = [];
    const semaphore = new Semaphore(this.maxConcurrentDownloads);

    for (let i = 0; i < segments.length; i++) {
      const segment = segments[i];

      downloadPromises.push(
        semaphore.acquire().then(async (release) => {
          try {
            const chunk = await this.downloadSegment(segment, streamData.headers, downloadTask);
            downloadedChunks[i] = chunk;
            downloadTask.downloadedSegments++;

            // 更新进度
            downloadTask.progress = (downloadTask.downloadedSegments / segments.length) * 100;
            if (downloadTask.progressCallback) {
              downloadTask.progressCallback({
                progress: downloadTask.progress,
                downloadedSegments: downloadTask.downloadedSegments,
                totalSegments: segments.length,
                downloadedSize: downloadTask.downloadedSize,
                totalSize: downloadTask.totalSize
              });
            }
          } catch (error) {
            console.error(`Failed to download segment ${i}:`, error);
            throw error;
          } finally {
            release();
          }
        })
      );
    }

    await Promise.all(downloadPromises);

    // 合并所有分片
    const finalBlob = await this.mergeSegments(downloadedChunks);

    // 触发下载
    await this.triggerDownload(finalBlob, `${(downloadTask.streamData.title || 'video')}.ts`);

    downloadTask.status = 'completed';
    if (downloadTask.completeCallback) {
      downloadTask.completeCallback({
        downloadId: downloadTask.id,
        totalSize: finalBlob.size,
        duration: Date.now() - downloadTask.startTime
      });
    }

    this.activeDownloads.delete(downloadTask.id);
  }

  /**
     * 下载单个分片
     * @param {Object} segment - 分片信息
     * @param {Object} headers - 请求头
     * @param {Object} downloadTask - 下载任务
     */
  async downloadSegment(segment, headers = {}, downloadTask) {
    for (let attempt = 1; attempt <= this.retryAttempts; attempt++) {
      try {
        const response = await fetch(segment.url, { headers });
        if (!response.ok) throw new Error(`Segment fetch failed: ${response.status}`);
        const buf = await response.arrayBuffer();
        let data = new Uint8Array(buf);

        if (segment.encryption && segment.encryption.method === 'AES-128') {
          const keyUri = segment.encryption.uri;
          let keyBytes = this.keyCache.get(keyUri);
          if (!keyBytes) {
            const keyResp = await fetch(keyUri, { headers });
            if (!keyResp.ok) throw new Error(`Key fetch failed: ${keyResp.status}`);
            const keyBuf = await keyResp.arrayBuffer();
            keyBytes = new Uint8Array(keyBuf);
            this.keyCache.set(keyUri, keyBytes);
          }

          const iv = segment.encryption.ivHex
            ? this.hexToBytes(segment.encryption.ivHex)
            : this.createIVFromNumber(segment.encryption.sequenceNumber || (downloadTask.sequenceStart + segment.index));

          const cryptoKey = await crypto.subtle.importKey(
            'raw',
            keyBytes,
            { name: 'AES-CBC' },
            false,
            ['decrypt']
          );
          const decrypted = await crypto.subtle.decrypt(
            { name: 'AES-CBC', iv },
            cryptoKey,
            data
          );
          data = new Uint8Array(decrypted);
        }

        downloadTask.downloadedSize += data.byteLength;
        return data;

      } catch (error) {
        if (attempt === this.retryAttempts) throw error;
        await this.delay(500 * attempt);
      }
    }
  }

  /**
     * 合并所有分片
     * @param {Array} chunks - 分片数组
     */
  async mergeSegments(chunks) {
    const validChunks = chunks.filter(chunk => chunk && chunk.length > 0);
    const totalLength = validChunks.reduce((sum, chunk) => sum + chunk.length, 0);

    const merged = new Uint8Array(totalLength);
    let offset = 0;

    for (const chunk of validChunks) {
      merged.set(chunk, offset);
      offset += chunk.length;
    }

    return new Blob([merged], { type: 'video/mp2t' });
  }

  /**
     * 触发文件下载
     * @param {Blob} blob - 文件数据
     * @param {string} filename - 文件名
     */
  async triggerDownload(blob, filename) {
    try {
      // 使用 Chrome Downloads API
      const url = URL.createObjectURL(blob);

      await chrome.downloads.download({
        url: url,
        filename: filename,
        saveAs: true
      });

      // 清理 URL
      setTimeout(() => URL.revokeObjectURL(url), 10000);
    } catch (error) {
      console.error('Failed to trigger download:', error);
      throw error;
    }
  }

  /**
     * 暂停下载
     * @param {string} downloadId - 下载ID
     */
  pauseDownload(downloadId) {
    const downloadTask = this.activeDownloads.get(downloadId);
    if (downloadTask) {
      downloadTask.status = 'paused';
    }
  }

  /**
     * 恢复下载
     * @param {string} downloadId - 下载ID
     */
  resumeDownload(downloadId) {
    const downloadTask = this.activeDownloads.get(downloadId);
    if (downloadTask && downloadTask.status === 'paused') {
      downloadTask.status = 'downloading';
    }
  }

  /**
     * 取消下载
     * @param {string} downloadId - 下载ID
     */
  cancelDownload(downloadId) {
    const downloadTask = this.activeDownloads.get(downloadId);
    if (downloadTask) {
      downloadTask.status = 'cancelled';
      this.activeDownloads.delete(downloadId);
    }
  }

  /**
     * 获取下载状态
     * @param {string} downloadId - 下载ID
     */
  getDownloadStatus(downloadId) {
    return this.activeDownloads.get(downloadId);
  }

  /**
     * 获取所有活跃下载
     */
  getActiveDownloads() {
    return Array.from(this.activeDownloads.values());
  }

  /**
     * 生成下载ID
     */
  generateDownloadId() {
    return 'download_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
  }

  /**
     * 延迟函数
     * @param {number} ms - 毫秒
     */
  delay(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
  }

  /**
     * 检测文件大小
     * @param {string} url - 文件URL
     * @param {Object} headers - 请求头
     */
  async detectFileSize(url, headers = {}) {
    try {
      const response = await fetch(url, {
        method: 'HEAD',
        headers
      });

      const contentLength = response.headers.get('content-length');
      return contentLength ? parseInt(contentLength) : null;
    } catch (error) {
      console.warn('Failed to detect file size:', error);
      return null;
    }
  }
}



// Utility: parse attribute list like: #EXT-X-KEY:METHOD=AES-128,URI="...",IV=0x...
HLSDownloader.prototype.parseAttributeList = function(line) {
  const idx = line.indexOf(':');
  if (idx < 0) return {};
  const rest = line.slice(idx + 1);
  const parts = rest.split(',');
  const attrs = {};
  for (const part of parts) {
    const eq = part.indexOf('=');
    if (eq < 0) continue;
    const k = part.slice(0, eq).trim();
    const v = part.slice(eq + 1).trim();
    attrs[k] = v;
  }
  return attrs;
};

// Utility: hex string to Uint8Array
HLSDownloader.prototype.hexToBytes = function(hex) {
  let clean = hex.replace(/^0x/i, '');
  if (clean.length % 2 === 1) clean = '0' + clean;
  const out = new Uint8Array(clean.length / 2);
  for (let i = 0; i < out.length; i++) {
    out[i] = parseInt(clean.substr(i * 2, 2), 16);
  }
  return out;
};

// Utility: build 16-byte IV from sequence number (big-endian)
HLSDownloader.prototype.createIVFromNumber = function(n) {
  const iv = new Uint8Array(16);
  let num = Number(n) || 0;
  for (let i = 15; i >= 0; i--) {
    iv[i] = num & 0xff;
    num = Math.floor(num / 256);
  }
  return iv;
};

// Utility: resolve possibly relative URI with baseUrl
HLSDownloader.prototype.resolveUrl = function(uri, baseUrl) {
  if (!uri) return uri;
  const unquoted = uri.replace(/"/g, '');
  if (/^https?:\/\//i.test(unquoted)) return unquoted;
  if (unquoted.startsWith('/')) {
    try {
      const b = new URL(baseUrl);
      return `${b.protocol}//${b.host}${unquoted}`;
    } catch {
      return baseUrl + unquoted.replace(/^\//, '');
    }
  }
  return baseUrl + unquoted;
};

/**
 * 信号量类，用于控制并发数量
 */
class Semaphore {
  constructor(count) {
    this.count = count;
    this.waiting = [];
  }

  async acquire() {
    return new Promise((resolve) => {
      if (this.count > 0) {
        this.count--;
        resolve(() => this.release());
      } else {
        this.waiting.push(() => {
          this.count--;
          resolve(() => this.release());
        });
      }
    });
  }

  release() {
    this.count++;
    if (this.waiting.length > 0) {
      const next = this.waiting.shift();
      next();
    }
  }
}

// 导出 HLS 下载器
if (typeof module !== 'undefined' && module.exports) {
  module.exports = HLSDownloader;
} else if (typeof window !== 'undefined') {
  window.HLSDownloader = HLSDownloader;
}
